package com.ruoyi.ucenter.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.model.auth.AuthContextHolder;
import com.ruoyi.model.enums.SexType;
import com.ruoyi.model.user.UserInformation;
import com.ruoyi.model.user.dto.*;
import com.ruoyi.model.user.form.UserInfoForm;
import com.ruoyi.ucenter.auth.JwtUtil;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.BeanCopyUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.model.user.UserInfo;
import com.ruoyi.model.user.vo.UserInfoVo;
import com.ruoyi.ucenter.mapper.UserInfoMapper;
import com.ruoyi.ucenter.service.ISmsCodeService;
import com.ruoyi.ucenter.service.IUserInfoService;
import com.ruoyi.ucenter.service.IUserInformationService;
import com.ruoyi.ucenter.utils.EmailValidator;
import com.ruoyi.ucenter.utils.FormUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author : 张怀秋
 **/
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UsefInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements IUserInfoService {

    @Resource
    private RedisCache redisCache;

    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    @Resource
    private final ISmsCodeService smsCodeService;

    private final IUserInformationService userInformationService;

    @Override
    public int saveInformation(UserInformation information) {
        String userId = AuthContextHolder.getUserId();
        information.setUserId(Long.valueOf(userId));
        return userInformationService.save(information) ? 1 : 0;
    }

    /**
     * 注册用户
     * 使用手机号注册 所有这里没有加密
     *
     * @param userPhoneRegisterDto 前端表单
     * @return R
     */
    @Override
    public R<String> phoneRegisterByDTO(UserPhoneRegisterDto userPhoneRegisterDto) {
        //校验参数
        if (StringUtils.isEmpty(userPhoneRegisterDto.getPhone())) {
            return R.fail("手机号码不能为空!");
        }
        if (!FormUtils.isMobile(userPhoneRegisterDto.getPhone())) {
            return R.fail("务必填写正确的手机格式!");
        }
        if (StringUtils.isEmpty(userPhoneRegisterDto.getCode())) {
            return R.fail("验证码不能为空!");
        }
        if (Boolean.FALSE.equals(smsCodeService.checkCode("phone:code:" + userPhoneRegisterDto.getPhone(), userPhoneRegisterDto.getCode()))) {
            return R.fail("验证码错误!");
        }
        if (this.baseMapper.selectCount(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getPhone, userPhoneRegisterDto.getPhone())) != 0) {
            return R.fail("该手机号已注册!");
        }
        //通过校验 保存数据
        UserInfo userInfo = new UserInfo();
        userInfo.setPhone(userPhoneRegisterDto.getPhone());
        userInfo.setAvatar("https://himg.bdimg.com/sys/portrait/item/public.1.ba5cd65e.C9LpYCQo75G2YAAJzH4Q2Q.jpg"); //默认头像
        userInfo.setStatus(0);
        this.baseMapper.insert(userInfo);
        return R.ok();
    }

    /**
     * 根据手机号码登录
     *
     * @param loginUserDto 前端表单
     * @return Map
     */
    @Override
    public Map<String, Object> loginByPhone(LoginUserDto loginUserDto) {
        if (!FormUtils.isMobile(loginUserDto.getPhoneOrEmail())) {
            throw new RuntimeException("手机号格式错误!");
        }
        UserInfo userInfo = this.baseMapper.selectOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getPhone, loginUserDto.getPhoneOrEmail()));
        if (ObjectUtils.isNotEmpty(userInfo)) {
            String code = redisCache.getCacheObject("phone:code:" + loginUserDto.getPhoneOrEmail());
            if (code != null && code.equals(loginUserDto.getCode())) {
                String json = JSONObject.toJSONString(userInfo.getId());
                String token = JwtUtil.createJWT(json);
                Long dates = System.currentTimeMillis();
                redisCache.setCacheObject("loginUser:" + userInfo.getId(), userInfo, 1, TimeUnit.DAYS);
                Map<String, Object> result = new HashMap<>();
                result.put("token", token);
                result.put("dates", dates);
                redisCache.deleteObject("phone:code:" + loginUserDto.getPhoneOrEmail());
                return result;
            } else {
                throw new RuntimeException("验证码错误!");
            }
        } else {
            throw new RuntimeException("该手机号尚未注册!");
        }
    }

    /**
     * 根据邮箱登录
     *
     * @param loginUserDto 前端表单
     * @return Map
     */
    @Override
    public Map<String, Object> loginByMail(LoginUserDto loginUserDto) {
        if (!FormUtils.isMall(loginUserDto.getPhoneOrEmail())) {
            throw new RuntimeException("邮箱格式错误!");
        }
        UserInfo userInfo = this.baseMapper.selectOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getEmail, loginUserDto.getPhoneOrEmail()));
        if (ObjectUtils.isNotEmpty(userInfo)) {
            String code = redisCache.getCacheObject("mail:code:" + loginUserDto.getPhoneOrEmail());
            if (code != null && code.equals(loginUserDto.getCode())) {
                String json = JSONObject.toJSONString(userInfo.getId());
                String token = JwtUtil.createJWT(json);
                Long dates = System.currentTimeMillis();
                redisCache.setCacheObject("loginUser:" + userInfo.getId(), userInfo, 1, TimeUnit.DAYS);
                Map<String, Object> result = new HashMap<>();
                result.put("token", token);
                result.put("dates", dates);
                redisCache.deleteObject("mail:code:" + loginUserDto.getPhoneOrEmail());
                return result;
            } else {
                throw new RuntimeException("验证码错误!");
            }
        } else {
            throw new RuntimeException("该手机号尚未注册!");
        }
    }


    /**
     * 根据平台账号密码登录
     *
     * @param loginUserDto 前端表单
     * @return Map
     */
    @Override
    public Map<String, Object> loginByApp(LoginUserDto loginUserDto) {
        UserInfo userInfo = this.baseMapper.selectOne(
                Wrappers.<UserInfo>lambdaQuery()
                        .eq(UserInfo::getPhone, loginUserDto.getPhoneOrEmail())
                        .or().eq(UserInfo::getEmail, loginUserDto.getPhoneOrEmail())
        );

//        UserInfo userInfo = this.baseMapper.selectOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getName, loginUserDto.getUsername()));
        if (ObjectUtils.isNotEmpty(userInfo)) {
            boolean matches = bCryptPasswordEncoder.matches(loginUserDto.getPassword(), userInfo.getPassword());
            if (matches) {
                long dates = System.currentTimeMillis();
                String json = JSONObject.toJSONString(userInfo.getId());
                String token = JwtUtil.createJWT(json);
                redisCache.setCacheObject("loginUser:" + userInfo.getId(), userInfo, 1, TimeUnit.DAYS);
                Map<String, Object> result = new HashMap<>();
                result.put("token", token);
                result.put("dates", dates);
                return result;
            } else {
                throw new RuntimeException("密码错误!");
            }
        } else {
            throw new RuntimeException("该账号不存在,请检查是否登录,或联系客服。");
        }
    }

    /**
     * 根据当前用户登录用户id获取到此人的详细信息
     *
     * @param userInfo 用户id
     * @return userInfoVo
     */
    @Override
    public UserInfoVo selectUserInfo(UserInfo userInfo) {
        List<UserInformation> userInformationList = this.userInformationService
                .list(Wrappers.<UserInformation>lambdaQuery()
                        .eq(UserInformation::getUserId, userInfo.getId()));
        UserInfoVo userInfoVo = BeanCopyUtils.copyBean(userInfo, UserInfoVo.class);
        userInfoVo.setUserInformationList(userInformationList);
        return userInfoVo;
    }

    /**
     * 修改用户基本信息 展示只考虑基本用户信息
     *
     * @param userId 用户id
     * @return 影响行数
     */
    @Override
    public int editUserInfoById(String userId, UserInfoForm userInfoForm) {
        UserInfo userInfo = this.baseMapper.selectById(userId);
        BeanUtils.copyProperties(userInfoForm, userInfo);
        System.out.println(userInfo);
        userInfo.setSex(userInfoForm.getSex() == 0 ? SexType.MALE.getCode() : SexType.FEMALE.getCode());
        redisCache.deleteObject("loginUser:" + userId);
        redisCache.setCacheObject("loginUser:" + userId, userInfo, 1, TimeUnit.DAYS);
        userInfo.setId(userId);
        return this.baseMapper.updateById(userInfo);
    }

    /**
     * 修改用户账号信息
     * @param userId      用户id
     * @param userInfoDto 用户个人信息
     */
    @Override
    public int editSecureUserInfo(String userId, UserInfoDto userInfoDto) {
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(userInfoDto, userInfo);
        if (userInfoDto.getPassword() != null && userInfoDto.getRePassword() != null) {
            if (!userInfoDto.getPassword().equals(userInfoDto.getRePassword())) {
                throw new RuntimeException("两次密码不一致!");
            }
        }
        userInfo.setPassword(bCryptPasswordEncoder.encode(userInfoDto.getPassword()));
        return this.baseMapper.updateById(userInfo);
    }

    @Override
    public int editPEP(int type, String checkCode, String change) {
        String userId = AuthContextHolder.getUserId();
        String cacheObject = redisCache.getCacheObject("user:edit:check" + userId);
        if (type != 3 && (cacheObject == null || cacheObject.equals("false"))) {
            throw new RuntimeException("非法操作!");
        }
        UserInfo userInfo = this.baseMapper.selectOne(
                Wrappers.<UserInfo>lambdaQuery()
                        .eq(UserInfo::getId, userId));
        switch (type) {
            //修改手机验证码
            case 1:
                if (Boolean.FALSE.equals(smsCodeService.checkCode("phone:code:" + userInfo.getPhone(), checkCode))) {
                    throw new RuntimeException("手机验证码错误!");
                }
                userInfo.setPhone(change);
                break;
            //邮箱验证码验证
            case 2:
                if (Boolean.FALSE.equals(smsCodeService.checkCode("mail:code:" + userInfo.getEmail(), checkCode))) {
                    throw new RuntimeException("邮箱验证码错误!");
                }
                userInfo.setEmail(change);
                break;
            case 3:
                userInfo.setPassword(bCryptPasswordEncoder.encode(checkCode));
                break;
            default:
                break;
        }
        redisCache.deleteObject("user:edit:check" + userId);
        return this.baseMapper.updateById(userInfo);
    }


    @Override
    public boolean checkPEP(int type, String checkCode) {
        Boolean check = false;
        String userId = AuthContextHolder.getUserId();
        UserInfo userInfo = this.baseMapper.selectOne(
                Wrappers.<UserInfo>lambdaQuery()
                        .eq(UserInfo::getId, userId));
        switch (type) {
            //手机验证码验证
            case 1:
                check = smsCodeService.checkCode("phone:code:" + userInfo.getPhone(), checkCode);
                break;
            //邮箱验证码验证
            case 2:
                check = smsCodeService.checkCode("mail:code:" + userInfo.getEmail(), checkCode);
                break;
            case 3:
                //密码验证
                check = checkPassword(userInfo.getPassword(), checkCode);
                break;
        }
        if (check) redisCache.setCacheObject("user:edit:check" + userId, "true");
        return check;
    }

    private Boolean checkPassword(String password, String rePassword) {
        return bCryptPasswordEncoder.matches(rePassword, password);
    }

    @Override
    public List<UserInfo> selectUserInfoList(UserInfo userInfo) {
        return baseMapper.selectUserInfoList(userInfo);
    }

    @Override
    public List<UserInformation> getInformation() {
        String userId = AuthContextHolder.getUserId();
        return userInformationService.list(Wrappers.<UserInformation>lambdaQuery().eq(UserInformation::getUserId, userId));
    }

    @Override
    public UserInformation getInformationById(String id) {
        String userId = AuthContextHolder.getUserId();
        return userInformationService.getOne(Wrappers.<UserInformation>lambdaQuery()
                .eq(UserInformation::getUserId, userId)
                .eq(UserInformation::getId, id)
        );
    }

    /**
     * 注册接口-邮箱注册
     *
     * @param userEmailRegisterDto 注册表单
     * @return R<String>
     */
    @Override
    public R<String> emailRegisterByDTO(UserEmailRegisterDto userEmailRegisterDto) {
        //断言触发条件 isXXX和内部条件不匹配就触发后面的message!
        Assert.isFalse(StringUtils.isEmpty(userEmailRegisterDto.getEmail()), "邮箱不能为空!");
        EmailValidator emailValidator = new EmailValidator();
        Assert.isTrue(emailValidator.validate(userEmailRegisterDto.getEmail()), "邮箱格式有误!");
        Assert.isFalse(StringUtils.isEmpty(userEmailRegisterDto.getCode()), "请填写验证码!");
        if (Boolean.FALSE.equals(smsCodeService.checkCode("mail:code:" + userEmailRegisterDto.getEmail(), userEmailRegisterDto.getCode()))) {
            return R.fail("验证码错误!");
        }
        if (this.baseMapper.selectCount(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getEmail, userEmailRegisterDto.getEmail())) != 0) {
            return R.fail("该邮箱已注册!");
        }
        //通过校验
        UserInfo userInfo = new UserInfo();
        userInfo.setEmail(userEmailRegisterDto.getEmail());
        userInfo.setAvatar("https://himg.bdimg.com/sys/portrait/item/public.1.ba5cd65e.C9LpYCQo75G2YAAJzH4Q2Q.jpg"); //默认头像
        userInfo.setStatus(0);
        this.baseMapper.insert(userInfo);
        return R.ok();
    }

    @Override
    public R<String> baseRegister(UserInfoRegisterDto userInfoDto) {
        // 判断校验
        String msg = validityUserInfo(userInfoDto);
        if (!"pass".equals(msg)) {
            return R.fail(msg);
        }
        // 校验通过
        // 1.1 注册
        UserInfo userInfo = new UserInfo();

        // 1.2 加密密码
        userInfo.setPassword(bCryptPasswordEncoder.encode(userInfoDto.getPassword()));
        userInfo.setAvatar(userInfoDto.getAvatar());
        userInfo.setPhone(userInfoDto.getPhone());
        userInfo.setEmail(userInfoDto.getEmail());
        userInfo.setNickName(userInfoDto.getNickName());
        userInfo.setName(userInfoDto.getName());
        userInfo.setStatus(0);// 正常
        // 1.3 添加用户信息
        this.baseMapper.insert(userInfo);
        return R.ok();
    }

    /**
     * 校验用户信息
     * @param data 用户信息
     */
    private String validityUserInfo(UserInfoRegisterDto data){
        String msg = "pass";
        if (StringUtils.isEmpty(data.getPassword())) {
            msg = "密码不能为空!";
        } else if (StringUtils.isEmpty(data.getQuirePassword())) {
            msg = "确认密码不能为空!";
        } else if (!data.getPassword().equals(data.getQuirePassword())) {
            msg = "两次密码不一致!";
        } else if (StringUtils.isEmpty(data.getNickName())) {
            msg = "昵称不能为空!";
        }
        // 判断手机和邮箱是否都为空
        if (StringUtils.isEmpty(data.getPhone()) && StringUtils.isEmpty(data.getEmail())){
            msg = "请选择电话或邮箱！";
        }
        // 如果手机号不为空
        if (!StringUtils.isEmpty(data.getPhone())){
            if (!FormUtils.isMobile(data.getPhone())) {
                msg = "务必填写正确的手机格式！";
            }
//            if (!StringUtils.isEmpty(data.getCode())) {
//                if (Boolean.FALSE.equals(smsCodeService.checkCode("phone:code:" + data.getPhone(), data.getCode()))) {
//                    msg = "验证码错误!";
//                }
//            }
            if (this.baseMapper.selectCount(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getPhone, data.getPhone())) != 0) {
                msg = "该手机号已注册!";
            }
        }
        if (!StringUtils.isEmpty(data.getEmail())){
            // 验证邮箱
            EmailValidator emailValidator = new EmailValidator();
            if (!FormUtils.isMall(data.getEmail())){
                msg = "邮箱格式有误!";
            }
//            if (!StringUtils.isEmpty(data.getCode())){
//                if (Boolean.FALSE.equals(smsCodeService.checkCode("mail:code:" + data.getEmail(), data.getCode()))) {
//                    msg = "验证码错误!";
//                }
//            }
            if (this.baseMapper.selectCount(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getEmail, data.getEmail())) != 0) {
                msg = "该邮箱已注册!";
            }
        }
        return msg;
    }
}
